package com.siam.package_merchant.service_impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.siam.package_common.constant.Quantity;
import com.siam.package_common.entity.BasicResult;
import com.siam.package_common.exception.StoneCustomerException;
import com.siam.package_common.util.GenerateNo;
import com.siam.package_merchant.auth.cache.MerchantSessionManager;
import com.siam.package_merchant.entity.Merchant;
import com.siam.package_merchant.entity.MerchantBillingRecord;
import com.siam.package_merchant.entity.MerchantWithdrawRecord;
import com.siam.package_merchant.mapper.MerchantWithdrawRecordMapper;
import com.siam.package_merchant.model.param.MerchantWithdrawRecordParam;
import com.siam.package_merchant.service.MerchantBillingRecordService;
import com.siam.package_merchant.service.MerchantService;
import com.siam.package_merchant.service.MerchantWithdrawRecordService;
import com.siam.package_order.feign.WxPayFeignApi;
import com.siam.package_user.entity.Member;
import com.siam.package_user.feign.MemberFeignApi;
import com.siam.package_user.feign.MemberTradeRecordFeignApi;
import com.siam.package_user.util.TokenUtil;
import com.siam.package_util.entity.Setting;
import com.siam.package_util.feign.SettingFeignApi;
import com.siam.package_weixin_pay.entity.TransferToUserRequest;
import com.siam.package_weixin_pay.entity.TransfersDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class MerchantWithdrawRecordServiceImpl extends ServiceImpl<MerchantWithdrawRecordMapper, MerchantWithdrawRecord> implements MerchantWithdrawRecordService {

    @Autowired
    private MerchantWithdrawRecordMapper merchantWithdrawRecordMapper;

    @Autowired
    private MerchantService merchantService;

    @Autowired
    private SettingFeignApi settingFeignApi;

    @Autowired
    private WxPayFeignApi wxPayFeignApi;

    @Autowired
    private MemberFeignApi memberFeignApi;

    @Autowired
    private MemberTradeRecordFeignApi memberTradeRecordFeignApi;

    @Autowired
    private MerchantBillingRecordService merchantBillingRecordService;

    @Autowired
    private MerchantSessionManager merchantSessionManager;

    @Override
    public void deleteByPrimaryKey(Integer id) {
        merchantWithdrawRecordMapper.deleteById(id);
    }

    @Override
    public void insert(MerchantWithdrawRecordParam param) {
        Merchant loginMerchant = merchantSessionManager.getSession(TokenUtil.getToken());
        Merchant dbMerchant = merchantService.selectByPrimaryKey(loginMerchant.getId());

        if(param.getWithdrawAmount().compareTo(BigDecimal.ZERO) <= 0){
            throw new StoneCustomerException("提现金额必须大于0");
        }

        // 校验是否有在途提现记录
        LambdaQueryWrapper<MerchantWithdrawRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MerchantWithdrawRecord::getMerchantId, dbMerchant.getId());
        queryWrapper.in(MerchantWithdrawRecord::getAuditStatus, MerchantWithdrawRecord.AUDIT_STATUS_PROCESSING, MerchantWithdrawRecord.AUDIT_STATUS_PAYMENT_PROCESSING);
        int count = this.count(queryWrapper);
        if(count > 0){
            throw new StoneCustomerException("您有提现记录正在审批，需要审批后才能再次提现！");
        }

        //自动计算平台手续费
        Setting setting = settingFeignApi.selectCurrent().getData();
        BigDecimal merchantWithdrawFee = setting.getMerchantWithdrawFee().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
        BigDecimal platformFee = param.getWithdrawAmount().multiply(merchantWithdrawFee).setScale(2, BigDecimal.ROUND_HALF_UP);
        BigDecimal totalAmount = param.getWithdrawAmount().add(platformFee).setScale(2, BigDecimal.ROUND_HALF_UP);

        BigDecimal actualAmount = param.getWithdrawAmount();
        if(totalAmount.compareTo(dbMerchant.getWithdrawableBalance()) > 0){
            actualAmount = param.getWithdrawAmount().subtract(platformFee).setScale(2, BigDecimal.ROUND_HALF_UP);
            totalAmount = param.getWithdrawAmount();
        }

        //判断余额是否充足
        if(totalAmount.compareTo(dbMerchant.getWithdrawableBalance()) > 0){
            throw new StoneCustomerException("余额不足，请重新填写提现金额");
        }

        // 获取订单编号
        String orderNo = GenerateNo.MERCHANT_FLAG + GenerateNo.getOrderNo();

        param.setMerchantId(dbMerchant.getId());
        param.setOrderNo(orderNo);
        param.setPlatformFee(platformFee);
        param.setActualAmount(actualAmount);
        param.setCreateTime(new Date());
        param.setUpdateTime(new Date());
        this.save(param);

        //减少商家的余额
        BigDecimal updateBalance = dbMerchant.getBalance().subtract(totalAmount).setScale(2, BigDecimal.ROUND_HALF_UP);;

        BigDecimal updateWithdrawableBalance = dbMerchant.getWithdrawableBalance().subtract(totalAmount).setScale(2, BigDecimal.ROUND_HALF_UP);;

        Merchant updateMerchant = new Merchant();
        updateMerchant.setId(dbMerchant.getId());
        updateMerchant.setBalance(updateBalance);
        updateMerchant.setWithdrawableBalance(updateWithdrawableBalance);
        updateMerchant.setUpdateTime(new Date());
        merchantService.updateByPrimaryKeySelective(updateMerchant);

        //增加商家账单记录
        MerchantBillingRecord merchantBillingRecord = new MerchantBillingRecord();
        merchantBillingRecord.setMerchantId(dbMerchant.getId());
        merchantBillingRecord.setType(Quantity.INT_2);
        merchantBillingRecord.setOperateType(Quantity.INT_2);
        merchantBillingRecord.setCoinType(Quantity.INT_1);
        merchantBillingRecord.setNumber(actualAmount);
        merchantBillingRecord.setServiceFee(param.getPlatformFee());
        merchantBillingRecord.setMessage("商家提现 -- 订单号" + param.getOrderNo());
        merchantBillingRecord.setCreateTime(new Date());
        merchantBillingRecordService.insertSelective(merchantBillingRecord);
    }

    @Override
    public MerchantWithdrawRecord selectByPrimaryKey(Integer id) {
        return merchantWithdrawRecordMapper.selectById(id);
    }

    @Override
    public void updateByPrimaryKeySelective(MerchantWithdrawRecord merchantWithdrawRecord) {
        merchantWithdrawRecordMapper.updateById(merchantWithdrawRecord);
    }

    @Override
    public Page getListByPage(MerchantWithdrawRecordParam param) {
        Merchant loginMerchant = merchantSessionManager.getSession(TokenUtil.getToken());
        param.setMerchantId(loginMerchant.getId());
        Page<Map<String, Object>> page = merchantWithdrawRecordMapper.getListByPage(new Page(param.getPageNo(), param.getPageSize()), param);
        return page;
    }

    @Override
    public Page getListByPageJoinShop(MerchantWithdrawRecordParam param) {
        Page<Map<String, Object>> page = merchantWithdrawRecordMapper.getListByPageJoinShop(new Page(param.getPageNo(), param.getPageSize()), param);
        return page;
    }

    @Override
    public Map<String, Object> statisticalAmount(MerchantWithdrawRecordParam param) {
        Merchant loginMerchant = merchantSessionManager.getSession(TokenUtil.getToken());
        if(loginMerchant != null){
            param.setMerchantId(loginMerchant.getId());
        }

        BigDecimal withdrawalSuccessfulAmount = merchantWithdrawRecordMapper.statisticalAmountByWithdrawalSuccessful(param);

        Map<String, Object> map = new HashMap<>();
        map.put("withdrawalSuccessfulAmount", withdrawalSuccessfulAmount);
        return map;
    }

    @Override
    public void auditApplyWithdraw(MerchantWithdrawRecordParam param) {
        BasicResult basicResult = new BasicResult();

        if(param.getStatus() == Quantity.INT_2 && StringUtils.isBlank(param.getOpinion())){
            throw new StoneCustomerException("审核不通过时，审核意见不能为空");
        }

        MerchantWithdrawRecord dbMerchantWithdrawRecord = this.selectByPrimaryKey(param.getId());
        if(dbMerchantWithdrawRecord.getAuditStatus() != Quantity.INT_1){
            throw new StoneCustomerException("该提现记录的审核状态错误");
        }

        Merchant dbMerchant = merchantService.selectByPrimaryKey(dbMerchantWithdrawRecord.getMerchantId());

        //TODO-进行提现操作
        if(param.getStatus() == Quantity.INT_1){
            //审核通过
            Member bindMember = memberFeignApi.selectByPrimaryKey(dbMerchant.getMemberId()).getData();
            String orderNo = dbMerchantWithdrawRecord.getOrderNo();

            // 商家转账到零钱
            TransferToUserRequest request = constructTransferToUserRequest(dbMerchantWithdrawRecord, dbMerchant, bindMember, orderNo);
            JSONObject transferJsonObject = wxPayFeignApi.payToBalanceV3(request).getData();
            if(transferJsonObject == null){
                throw new StoneCustomerException("打款失败，请联系管理员");
            }

            // 组装extinfo
            Map<String, Object> extInfo = dbMerchantWithdrawRecord.getExtInfo() == null ? new HashMap<>() : JSON.parseObject(dbMerchantWithdrawRecord.getExtInfo(), Map.class);
            extInfo.put("requestMerchantTransferParams", transferJsonObject);

            //修改商家申请信息
            MerchantWithdrawRecord updateMerchantWithdrawRecord = new MerchantWithdrawRecord();
            updateMerchantWithdrawRecord.setId(param.getId());
            updateMerchantWithdrawRecord.setAuditStatus(MerchantWithdrawRecord.AUDIT_STATUS_PAYMENT_PROCESSING);
            updateMerchantWithdrawRecord.setAuditTime(new Date());
            updateMerchantWithdrawRecord.setExtInfo(JSON.toJSONString(extInfo));
            updateMerchantWithdrawRecord.setUpdateTime(new Date());
            this.updateByPrimaryKeySelective(updateMerchantWithdrawRecord);

        }else if(param.getStatus() == Quantity.INT_2){
            //审核不通过
            //修改商家申请信息
            MerchantWithdrawRecord updateMerchantWithdrawRecord = new MerchantWithdrawRecord();
            updateMerchantWithdrawRecord.setId(param.getId());
            updateMerchantWithdrawRecord.setAuditStatus(MerchantWithdrawRecord.AUDIT_STATUS_FAIL);
            updateMerchantWithdrawRecord.setAuditReason(param.getOpinion());
            updateMerchantWithdrawRecord.setAuditTime(new Date());
            updateMerchantWithdrawRecord.setUpdateTime(new Date());
            this.updateByPrimaryKeySelective(updateMerchantWithdrawRecord);

            // 提现失败后置处理
            handleWithdrawFail(dbMerchantWithdrawRecord, dbMerchant);
        }
    }

    public void handleWithdrawFail(MerchantWithdrawRecord dbMerchantWithdrawRecord, Merchant dbMerchant) {
        //退回商家提现扣除的金额
        BigDecimal totalAmount = dbMerchantWithdrawRecord.getActualAmount().add(dbMerchantWithdrawRecord.getPlatformFee()).setScale(2, BigDecimal.ROUND_HALF_UP);

        //增加商家的余额
        BigDecimal updateBalance = dbMerchant.getBalance().add(totalAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
        ;

        BigDecimal updateWithdrawableBalance = dbMerchant.getWithdrawableBalance().add(totalAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
        ;

        Merchant updateMerchant = new Merchant();
        updateMerchant.setId(dbMerchant.getId());
        updateMerchant.setBalance(updateBalance);
        updateMerchant.setWithdrawableBalance(updateWithdrawableBalance);
        updateMerchant.setUpdateTime(new Date());
        merchantService.updateByPrimaryKeySelective(updateMerchant);

        //增加商家账单记录
        MerchantBillingRecord merchantBillingRecord = new MerchantBillingRecord();
        merchantBillingRecord.setMerchantId(dbMerchantWithdrawRecord.getMerchantId());
        merchantBillingRecord.setType(Quantity.INT_3);
        merchantBillingRecord.setOperateType(Quantity.INT_1);
        merchantBillingRecord.setCoinType(Quantity.INT_1);
        merchantBillingRecord.setNumber(totalAmount);
        merchantBillingRecord.setMessage("商家提现失败退回 -- 订单号" + dbMerchantWithdrawRecord.getOrderNo());
        merchantBillingRecord.setCreateTime(new Date());
        merchantBillingRecordService.insertSelective(merchantBillingRecord);

        //TODO-发消息通知用户
    }

    /**
     * 企业付款到零钱-组装参数【已废弃，微信官方不再支持该功能】
     * @param dbMerchantWithdrawRecord
     * @param dbMerchant
     * @param bindMember
     * @param orderNo
     * @return
     */
    private TransfersDto constructTransfersDto(MerchantWithdrawRecord dbMerchantWithdrawRecord, Merchant dbMerchant, Member bindMember, String orderNo) {
        TransfersDto transfersDto = new TransfersDto();
        transfersDto.setOpenid(bindMember.getOpenId());
        transfersDto.setAmount(dbMerchantWithdrawRecord.getWithdrawAmount().doubleValue());
        transfersDto.setRe_user_name(dbMerchant.getRealName());
        transfersDto.setPartner_trade_no(orderNo);
        transfersDto.setDesc("高品外卖-商家提现到账");
        return transfersDto;
    }

    /**
     * 商家转账到零钱-组装参数
     * @param dbMerchantWithdrawRecord
     * @param dbMerchant
     * @param bindMember
     * @param orderNo
     * @return
     */
    private TransferToUserRequest constructTransferToUserRequest(MerchantWithdrawRecord dbMerchantWithdrawRecord, Merchant dbMerchant, Member bindMember, String orderNo) {
        // TODO: 请准备商户开发必要参数，参考：https://pay.weixin.qq.com/doc/v3/merchant/4013070756
        TransferToUserRequest request = new TransferToUserRequest();
        request.outBillNo = orderNo;
        request.transferSceneId = "1000";
        request.openid = bindMember.getOpenId();
        request.setUserName(dbMerchant.getRealName());
        request.transferAmount = dbMerchantWithdrawRecord.getWithdrawAmount().multiply(BigDecimal.valueOf(100)).longValue();
        request.transferRemark = "高品外卖-商家余额提现";
        request.transferSceneReportInfos = new ArrayList<>();
        {
            TransferToUserRequest.TransferSceneReportInfo item0 = new TransferToUserRequest.TransferSceneReportInfo();
            item0.infoType = "岗位类型";
            item0.infoContent = "-";
            request.transferSceneReportInfos.add(item0);
            TransferToUserRequest.TransferSceneReportInfo item1 = new TransferToUserRequest.TransferSceneReportInfo();
            item1.infoType = "报酬说明";
            item1.infoContent = "-";
            request.transferSceneReportInfos.add(item1);
        };
        return request;
    }
}